﻿using System;
using System.Configuration;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using System.Data.Common;
using System.Data.SqlClient;
using System.Net.Mail;

using Toenda.Foundation.Data;
using Toenda.Foundation.Types;

namespace Toenda.Foundation.Messaging {
	/// <summary>
	/// Provides functions to manage messages like sending emails, saving internal messages, get this internal messages and other.
	/// </summary>
	public class MessageManager : IMessagingInfo {
		/// <summary>
		/// Initializes a new instance of the <see cref="MessageManager"/> class.
		/// </summary>
		public MessageManager() {
		}

		#region Public Members

		/// <summary>
		/// Sends a message.
		/// </summary>
		/// <param name="message">The parameter message can be a <see cref="SystemMessage" /> or a <see cref="EmailMessage" />.</param>
		/// <returns></returns>
		public bool SendMessage(Message message) {
			bool sendResult = false;

			if(message == null) {
				throw new ArgumentNullException("message", "Message cannot be null!");
			}

			SystemMessage sysMsg = message as SystemMessage;
			EmailMessage emailMsg = message as EmailMessage;

			if(sysMsg == null && emailMsg == null) {
				throw new Exception("Message is null or not a SystemMessage or EmailMessage!");
			}

			if(sysMsg != null) {
				sendResult = this.SendSystemMessage(sysMsg);
			}

			if(emailMsg != null) {
				sendResult = this.SendEmailMessage(emailMsg);
			}

			return sendResult;
		}

		/// <summary>
		/// Loads all <see cref="SystemMessage"/>s from a user.
		/// </summary>
		/// <param name="userId">The user id.</param>
		/// <param name="fromUserId">The id of the user who is the creator of the messages.</param>
		/// <param name="sortParams">The sort params.</param>
		/// <returns></returns>
		public ICollection<Message> GetMessages(int? userId, int? fromUserId, IList<MessageSortParam> sortParams) {
			if(userId.HasValue) {
				List<Message> list = new List<Message>();

				#region Handle sort parameters.

				string orderClause;
				StringBuilder orderClauseBuilder = new StringBuilder(" ");

				for(int i = 0; i < sortParams.Count; i++) {
					MessageSortParam sortParam = sortParams[i];

					if(i != 0) {
						orderClauseBuilder.Append(", ");
					}

					string fieldName;
					switch(sortParam.Expression.ToLower()) {
						case "sortbysender":
							// add lastname
							fieldName = "[usr].[lastName]";

							orderClauseBuilder.Append(fieldName);

							switch(sortParam.Direction) {
								case DataSortDirection.Ascending:
									orderClauseBuilder.Append(" ASC");
									break;

								case DataSortDirection.Descending:
									orderClauseBuilder.Append(" DESC");
									break;

								default:
									throw new Exception(string.Format("Sort direction {0} not supported.", sortParam.Direction));
							}

							// add firstname
							fieldName = ", [usr].[firstName]";

							orderClauseBuilder.Append(fieldName);

							switch(sortParam.Direction) {
								case DataSortDirection.Ascending:
									orderClauseBuilder.Append(" ASC");
									break;

								case DataSortDirection.Descending:
									orderClauseBuilder.Append(" DESC");
									break;

								default:
									throw new Exception(string.Format("Sort direction {0} not supported.", sortParam.Direction));
							}
							break;

						case "timestamp":
							fieldName = "[msg].[" + sortParam.Expression + "]";

							orderClauseBuilder.Append(fieldName);

							switch(sortParam.Direction) {
								case DataSortDirection.Ascending:
									orderClauseBuilder.Append(" ASC");
									break;

								case DataSortDirection.Descending:
									orderClauseBuilder.Append(" DESC");
									break;

								default:
									throw new Exception(string.Format("Sort direction {0} not supported.", sortParam.Direction));
							}
							break;

						default:
							throw new ArgumentException(string.Format("Sort expression {0} not supported.", sortParam.Expression));
					}
				}

				orderClause = orderClauseBuilder.ToString();

				#endregion

				using(DAL dal = DAL.FromConfiguration("QBuS")) {
					DbCommand cmd = dal.CreateCommand();

					cmd.CommandText = new SqlResources()["GetMessages"];
					cmd.CommandText += new SqlResources()["GetMessages_Order"];
					cmd.CommandText += orderClause;

					cmd.AddParameter("userId", userId.Value);

					dal.OpenConnection();

					using(IDataReader reader = dal.ExecuteQueryForDataReader(cmd)) {
						while(reader.Read()) {
							list.Add(new SystemMessage(reader));
						}
					}
				}

				return list;
			}
			else {
				throw new Exception("UserId is null!");
			}
		}

		/// <summary>
		/// Changes the message property isRead to a specific value.
		/// </summary>
		/// <param name="messageId">The id of the specific message.</param>
		/// <param name="isRead">If its true, the message state is changed to read, if false, the state is changed to unread.</param>
		public void ChangeMessageRead(long messageId, bool isRead) {
			using(DAL dal = DAL.FromConfiguration("QBuS")) {
				DbCommand cmd = dal.CreateCommand();
				cmd.CommandText = new SqlResources()["ChangeMessageRead"];
				cmd.AddParameter("messageId", messageId);
				cmd.AddParameter("isRead", isRead);

				dal.OpenConnection();

				dal.ExecuteNonQuery(cmd);
			}
		}

		/// <summary>
		/// Deletes a specific message.
		/// </summary>
		/// <param name="messageId">The id of the message to delete.</param>
		/// <returns></returns>
		public bool DeleteMessage(long messageId) {
			bool sendResult = false;

			using(DAL dal = DAL.FromConfiguration("QBuS")) {
				DbCommand cmd = dal.CreateCommand();
				cmd.CommandText = new SqlResources()["DeleteMessage"];
				cmd.AddParameter("id", messageId);

				dal.OpenConnection();

				dal.ExecuteNonQuery(cmd);
			}

			return sendResult;
		}

		/// <summary>
		/// Loads a single message.
		/// </summary>
		/// <param name="messageId">The id of the message you want to load.</param>
		/// <returns></returns>
		public Message GetMessage(long messageId) {
			Message msg = null;

			using(DAL dal = DAL.FromConfiguration("QBuS")) {
				DbCommand cmd = dal.CreateCommand();

				cmd.CommandText = new SqlResources()["GetMessage"];

				cmd.AddParameter("id", messageId);

				dal.OpenConnection();

				using(IDataReader reader = dal.ExecuteQueryForDataReader(cmd)) {
					if(reader.Read()) {
						msg = new SystemMessage(reader);
					}
				}
			}

			return msg;
		}

		#endregion

		#region Private Members

		/// <summary>
		/// Sends the <see cref="EmailMessage" />.
		/// </summary>
		/// <param name="email"></param>
		/// <returns></returns>
		private bool SendEmailMessage(EmailMessage email) {
			bool sendResult = false;

			// check message body
			if(email.FromEmail.IsNullOrTrimmedEmpty()) {
				throw new Exception("The property FromEmail cannot be null!");
			}

			if(email.TargetEmailList == null
			|| email.TargetEmailList.Count < 1
			|| email.TargetEmailList[0].IsNullOrTrimmedEmpty()) {
				throw new Exception("The property TargetEmailList cannot be null or empty!");
			}

			// create mail
			MailMessage mail = new MailMessage();

			// add mail addresses
			mail.From = new MailAddress(email.FromEmail);

			foreach(string targetMail in email.TargetEmailList) {
				mail.To.Add(new MailAddress(targetMail));
			}

			// mail message & subject
			mail.Subject = (!email.Title.IsNullOrTrimmedEmpty() ? email.Title + " - " : "") 
				+ email.Subject 
				+ " (" + email.Timestamp.ToString() + ")";
			mail.Body = email.Text;

			// send per smtp
			try {
				string host = ConfigurationManager.AppSettings["mailServer"];

				SmtpClient smtp = new SmtpClient(host);
				smtp.Send(mail);

				sendResult = true;
			}
			catch(Exception ex) {
				Console.WriteLine(ex.ToString());
				sendResult = false;
			}

			return sendResult;
		}

		/// <summary>
		/// Sends the <see cref="SystemMessage" />.
		/// </summary>
		/// <param name="message"></param>
		/// <returns></returns>
		private bool SendSystemMessage(SystemMessage message) {
			bool sendResult = false;

			using(DAL dal = DAL.FromConfiguration("QBuS")) {
				DbCommand cmd = dal.CreateCommand();

				cmd.CommandText = new SqlResources()["SaveMessage"];
				cmd.AddParameter("timestamp", DateTime.Now);
				cmd.AddParameter("title", message.Title);
				cmd.AddParameter("subject", message.Subject);
				cmd.AddParameter("text", message.Text);
				cmd.AddParameter("ticketId", message.TicketId);
				cmd.AddParameter("userId", message.UserId);
				cmd.AddParameter("fromUserId", message.FromUserId);

				dal.OpenConnection();

				if(dal.ExecuteNonQuery(cmd) < 1) {
					sendResult = false;
				}
				else {
					sendResult = true;
				}
			}

			return sendResult;
		}

		#endregion
	}
}
